package com.yugao.fintech.antelope.infra.dict.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yugao.fintech.antelope.infra.dict.assembly.DictAssembly;
import com.yugao.fintech.antelope.infra.dict.manager.DictItemManager;
import com.yugao.fintech.antelope.infra.dict.manager.DictManager;
import com.yugao.fintech.antelope.infra.dict.service.DictService;
import com.yugao.fintech.antelope.infra.group.service.DataGroupService;
import com.yugao.fintech.antelope.infra.module.dict.model.entity.Dict;
import com.yugao.fintech.antelope.infra.module.dict.model.entity.DictItem;
import com.yugao.fintech.antelope.infra.module.dict.model.rqrs.DictItemResp;
import com.yugao.fintech.antelope.infra.module.dict.model.rqrs.DictQueryReq;
import com.yugao.fintech.antelope.infra.module.dict.model.rqrs.DictResp;
import com.yugao.fintech.antelope.infra.module.group.model.entity.DataGroup;
import com.yugao.fintech.antelope.infra.module.group.model.rqrs.DataGroupDeleteReq;
import com.yugao.fintech.antelope.infra.module.group.model.rqrs.DataGroupListReq;
import com.yugao.fintech.antelope.infra.module.group.model.rqrs.DataGroupResp;
import com.yugao.fintech.antelope.base.model.BasePlusPO;
import com.yugao.fintech.antelope.base.model.constants.CommonCons;
import com.yugao.fintech.antelope.base.model.enums.DataGroupBizTypeEnum;
import com.yugao.fintech.framework.assistant.utils.StringUtils;
import com.yugao.fintech.framework.assistant.utils.exception.Assert;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.core.annotations.FieldUniqueCheck;
import com.yugao.fintech.framework.core.enums.OperationTypeEnum;
import com.yugao.fintech.framework.mybatisplus.utils.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Service
public class DictServiceImpl implements DictService {
    @Autowired
    private DictManager dictManager;
    @Autowired
    private DictItemManager dictItemManager;
    @Autowired
    private DictAssembly dictAssembly;
    @Autowired
    private DataGroupService dataGroupService;

    public LambdaQueryWrapper<Dict> getQueryWrapper(DictQueryReq query) {
        Assert.notNull(query, "非法数据");
        LocalDateTime beginTime = query.getBeginLocalDateTime();
        LocalDateTime endTime = query.getEndLocalDateTime();
        return Dict.lqw()
                .like(StringUtils.isNotEmpty(query.getDictName()), Dict::getDictName, query.getDictName())
                .eq(StringUtils.isNotEmpty(query.getStatus()), Dict::getStatus, query.getStatus())
                .eq(StringUtils.isNotEmpty(query.getGroupId()), Dict::getGroupId, query.getGroupId())
                .like(StringUtils.isNotEmpty(query.getDictCode()), Dict::getDictCode, query.getDictCode())
                .ge(!Objects.isNull(beginTime), BasePlusPO::getCreatedAt, beginTime)
                .le(!Objects.isNull(endTime), BasePlusPO::getCreatedAt, endTime).orderByDesc(BasePlusPO::getCreatedAt);
    }

    @Override
    public List<Dict> list(DictQueryReq query) {
        return dictManager.list(getQueryWrapper(query));
    }

    @Override
    public IPage<DictResp> pageDict(DictQueryReq req) {
        IPage<Dict> respList = dictManager.page(PageUtil.to(req), getQueryWrapper(req));
        List<Long> groupIds = respList.getRecords().stream().map(Dict::getGroupId).distinct().collect(Collectors.toList());
        groupIds.add(-1L);
        Map<Long, String> groupMap = dataGroupService.listByIds(groupIds).stream()
                .collect(Collectors.toMap(DataGroup::getId, DataGroup::getName));
        return PageUtil.to(respList, (e) -> {
            DictResp resp = dictAssembly.toDictResp(e);
            resp.setGroupName(groupMap.get(resp.getGroupId()));
            return resp;
        });
    }

    @Override
    public List<Dict> listAll() {
        return dictManager.list(new QueryWrapper<>());
    }

    @Override
    public Map<String, List<DictItem>> listAllDictItem() {

        Map<String, List<DictItem>> data = new HashMap<>();
        List<Dict> dictList = dictManager.list(Dict.lqw());
        Map<String, List<DictItem>> dictItemMap = dictItemManager.list(DictItem.lqw())
                .stream().collect(Collectors.groupingBy(DictItem::getDictCode));
        for (Dict dict : dictList) {
            List<DictItem> dictItemList = dictItemMap.getOrDefault(dict.getDictCode(), Collections.emptyList());
            data.put(dict.getDictCode(), dictItemList);
        }
        return data;
    }

    @Override
    public Dict getById(Long dictId) {
        return dictManager.getOne(Dict.lqw().eq(Dict::getId, dictId));
    }

    @Override
    public Dict getByCode(String dictCode) {
        LambdaQueryWrapper<Dict> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Dict::getDictCode, dictCode);
        return dictManager.getOne(lqw);
    }

    @Override
    @Transactional
    public void deleteBatchByIds(Long[] dictIds) {
        List<Long> collect = Arrays.stream(dictIds).collect(Collectors.toList());
        dictManager.removeByIds(collect);
        dictItemManager.deleteByDictIds(collect);
    }

    @Override
    @Transactional
    public void deleteById(Long dictId) {
        dictManager.removeById(dictId);
        dictItemManager.deleteByDictId(dictId);
    }

    @Override
//    @CacheEvict(value = SysCacheCons.SYS_CONFIG, allEntries = true)
    public void resetDictCache() {

    }

    @FieldUniqueCheck(type = OperationTypeEnum.SAVE)
    @Override
    public boolean save(Dict dict) {
        initDefaultGroup(dict);
        return dictManager.save(dict);
    }

    @FieldUniqueCheck(type = OperationTypeEnum.UPDATE)
    @Override
//    @Cacheable(value = SysCacheCons.DICT, key = "#id + '_id'", unless = "#result == null")
    public boolean updateById(Dict dict) {
        initDefaultGroup(dict);
        return dictManager.updateById(dict);
    }

    public void initDefaultGroup(Dict dict) {
        if (Objects.isNull(dict.getGroupId())) {
            DataGroupListReq listReq = new DataGroupListReq().setBizType(DataGroupBizTypeEnum.DICT.getType())
                    .setCode(CommonCons.DataGroup.DEFAULT_CODE);
            DataGroupResp defaultGroup = dataGroupService.listGroup(listReq).stream().findFirst().orElseThrow(() -> new BizException("默认组不存在"));
            dict.setGroupId(defaultGroup.getId());
        }
    }


    @Override
    public List<DictResp> listExportData(DictQueryReq query) {
        List<Dict> dictList = this.list(query);
        List<Long> dictIds = dictList.stream().map(Dict::getId).collect(Collectors.toList());
        Map<Long, List<DictItemResp>> dictItemMap = dictItemManager.list(DictItem.lqw().in(DictItem::getDictId, dictIds))
                .stream().map(dictAssembly::toDictItemResp)
                .collect(Collectors.groupingBy(DictItemResp::getDictId));
        return dictList.stream().map(dictAssembly::toDictResp)
                .peek(e -> e.setDictItemList(dictItemMap.get(e.getId())))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public String importExcel(List<DictResp> importDict) {
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();

        Map<String, Dict> dictRecordMap = this.listAll().stream()
                .collect(Collectors.toMap(Dict::getDictCode, Function.identity()));

        for (DictResp e : importDict) {
            try {
                if (dictRecordMap.containsKey(e.getDictCode())) {
                    failureMsg.append("<br/>").append(failureNum++).append("、字典编码【").append(e.getDictCode()).append("】已存在");
                    continue;
                }
                Dict newDict = dictAssembly.toDict(e);
                this.save(newDict);

                Map<String, DictItem> dictItemMap = dictItemManager.list(DictItem.lqw().eq(DictItem::getDictCode, e.getDictCode()))
                        .stream().collect(Collectors.toMap(DictItem::getItemValue, Function.identity()));

                List<DictItem> saveDictItem = new ArrayList<>();
                int dictItemFailNum = 0;
                for (DictItemResp importDictItem : e.getDictItemList()) {
                    if (dictItemMap.containsKey(importDictItem.getItemValue())) {
                        failureMsg.append("<br/>").append(failureNum).append(".").append(++dictItemFailNum).append("、字典编码【")
                                .append(e.getDictCode()).append("】中的字典值【").append(importDictItem.getItemValue()).append("】已存在");
                        continue;
                    }
                    importDictItem.setDictId(newDict.getId());
                    saveDictItem.add(dictAssembly.toDictItem(importDictItem));
                }
                dictItemManager.saveBatch(saveDictItem);

            } catch (Exception exception) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、字典编码 " + e.getDictCode() + " 导入失败：";
                failureMsg.append(msg).append(exception.getMessage());
                log.error(msg, e);
            }
        }

        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new BizException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条");
        }
        return successMsg.toString();
    }

    @Override
    public boolean deleteGroup(DataGroupDeleteReq req) {
        DataGroup firstGroup = dataGroupService.listByIds(req.getIds()).stream().findFirst()
                .orElseThrow(() -> new BizException("删除的数据不存在"));

        DataGroupListReq listReq = new DataGroupListReq();
        listReq.setBizType(DataGroupBizTypeEnum.DICT.getType())
                .setGroupType(firstGroup.getGroupType())
                .setCode(CommonCons.DataGroup.DEFAULT_CODE);
        DataGroupResp defaultGroup = dataGroupService.listGroup(listReq).stream().findFirst()
                .orElseThrow(() -> new BizException("未查询到默认分组, 请联系管理员"));

        // 将被删除的组下面数据移动到默认分组下
        this.dictManager.update(Dict.luw().set(Dict::getGroupId, defaultGroup.getId()).in(Dict::getGroupId, req.getIds()));
        return dataGroupService.deleteGroup(req);
    }
}
